﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UserActioner.cs" company="DissingNelleman">
//   
// All rights reserved. </copyright>
// <summary>
//   The user actioner.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ServiceLibrary.Actioners.Base
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using NHibernate;
    using NHibernate.Criterion;

    using ServiceLibrary.Models;
    using ServiceLibrary.Models.Base;
    using ServiceLibrary.Models.Domain;
    using ServiceLibrary.Services.Commands;
    using ServiceLibrary.Services.Queries;
    using ServiceLibrary.Services.Results;

    /// <summary>
    /// The user actioner.
    /// </summary>
    public class UserActioner : ActionerBase
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UserActioner"/> class.
        /// </summary>
        /// <param name="session">
        /// The session.
        /// </param>
        /// <param name="activeUser">
        /// The active user.
        /// </param>
        public UserActioner(ISession session, User activeUser)
            : base(session, activeUser)
        {
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The create user.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <returns>
        /// The <see cref="Guid"/>.
        /// </returns>
        public Guid CreateUser(CreateUserCommand command)
        {
            var user = new User();
            user.ID = command.User.ID;
            user.Username = command.User.Username;
            user.Name = command.User.Name;
            user.CreatedDateTime = DateTime.Now;
            user.EncryptedPassword = command.EncryptedPassword;
            user.Email = command.User.Email;
            user.PhoneNumber = command.User.PhoneNumber;
            user.IsActive = true;
            this.Session.Save(user);
            this.Log.CreateLogEntry(
                user.ID, string.Format("Bruger '{0}', blev oprettet.", user.Name), Enums.LogEntryType.User);

            return user.ID;
        }

        /// <summary>
        /// The execute search query.
        /// </summary>
        /// <param name="query">
        /// The query.
        /// </param>
        /// <returns>
        /// The <see cref="UsersResult"/>.
        /// </returns>
        public UsersResult ExecuteSearchQuery(UsersQuery query)
        {
            SearchSettings settings = query.Settings;

            var result = new UsersResult();

            IQueryOver<User, User> search = this.Session.QueryOver<User>();
            this.ApplyDefaultCriterias(settings, search);

            result.TotalRowCount = search.RowCount();

            if (query.OnlyActiveUsers.HasValue && query.OnlyActiveUsers.Value)
            {
                search = search.Where(x => x.IsActive == true);
            }

            if (query.MailIsLike != null)
            {
                search = search.WhereRestrictionOn(x => x.Email)
                               .IsInsensitiveLike(query.MailIsLike, MatchMode.Anywhere);
            }

            if (query.NameIsLike != null)
            {
                search = search.WhereRestrictionOn(x => x.Name)
                               .IsInsensitiveLike(query.NameIsLike, MatchMode.Anywhere);
            }

            if (query.UsernameEquals != null)
            {
                search = search.WhereRestrictionOn(x => x.Username)
                               .IsInsensitiveLike(query.UsernameEquals, MatchMode.Exact);
            }

            var users = this.Sort(settings, ApplySkipAndTake(settings, search));

            foreach (User user in users)
            {
                Services.Dto.User dto = Services.Dto.User.Convert(user);
                if (dto != null)
                {
                    result.List.Add(dto);
                }
            }

            return result;
        }

        /// <summary>
        /// The get user.
        /// </summary>
        /// <param name="id">
        /// The id.
        /// </param>
        /// <returns>
        /// The <see cref="User"/>.
        /// </returns>
        public User GetUser(Guid id)
        {
            var type = this.Session.Get<User>(id);

            return type;
        }

        /// <summary>
        /// The update password.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        public void UpdatePassword(UpdateUserPasswordCommand command)
        {
            var user = this.Session.Get<User>(command.ID);
            user.EncryptedPassword = command.NewPassword;
            this.Session.Update(user);
        }

        /// <summary>
        /// The update user.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <returns>
        /// The <see cref="Guid"/>.
        /// </returns>
        public Guid UpdateUser(UpdateUserCommand command)
        {
            User user = this.GetUser(command.UpdatedUser.ID);

            if (user.ID != Guid.Empty)
            {
                var query = new UsersQuery();
                query.UsernameEquals = command.UpdatedUser.Username;
                query.Settings = new SearchSettings();
                query.Settings.IdNotEqual = user.ID;
                UsersResult res = this.ExecuteSearchQuery(query);

                if (res != null && res.List.Count() == 0)
                {
                    user.Username = command.UpdatedUser.Username;
                }

                user.Name = command.UpdatedUser.Name;
                user.Email = command.UpdatedUser.Email;
                user.PhoneNumber = command.UpdatedUser.PhoneNumber;
                this.Session.Update(user);
                this.Log.CreateLogEntry(
                    user.ID, string.Format("Bruger '{0}', blev redigéret.", user.Name), Enums.LogEntryType.User);
            }

            return user.ID;
        }

        /// <summary>
        /// The validate login.
        /// </summary>
        /// <param name="query">
        /// The query.
        /// </param>
        /// <returns>
        /// The <see cref="ValidateUserResult"/>.
        /// </returns>
        public ValidateUserResult ValidateLogin(ValidateUserQuery query)
        {
            User user =
                this.Session.QueryOver<User>()
                    .WhereRestrictionOn(x => x.Username)
                    .IsLike(query.Username)
                    .SingleOrDefault();

            var set = new ValidateUserResult();
            set.UserID = Guid.Empty;

            if (user != null)
            {
                string encryptedPassword = MD5Encrypter.EncryptPassword(user.ID.ToString(), query.Password);
                if (encryptedPassword.Equals(user.EncryptedPassword))
                {
                    set.UserID = user.ID;
                }
            }

            return set;
        }

        /// <summary>
        /// The validate login boolean.
        /// </summary>
        /// <param name="query">
        /// The query.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public bool ValidateLoginBoolean(ValidateUserQuery query)
        {
            bool isValid = !this.ValidateLogin(query).UserID.Equals(Guid.Empty);

            return isValid;
        }


        public void DeactiveUser(DeactivateUserCommand cmd)
        {
            if (cmd.ID == null || cmd.ID == Guid.Empty)
            {
                return; // No user - no problem
            }
                var user = this.Session.Get<User>(cmd.ID);

                if (user == null)
                {
                    return; // No user - no problem
                }

                user.Username = string.Format("{0} - {1} - Deactivated by {2}", user.Username, DateTime.Now.ToShortDateString(), this.ActiveUser.Username);
                user.IsActive = false;
                this.Session.Update(user);
        }
        #endregion

        #region Methods

        /// <summary>
        /// The sort.
        /// </summary>
        /// <param name="settings">
        /// The settings.
        /// </param>
        /// <param name="list">
        /// The list.
        /// </param>
        /// <returns>
        /// The ordered list of users.
        /// </returns>
        private IEnumerable<User> Sort(SearchSettings settings, IEnumerable<User> list)
        {
            if (settings != null && settings.OrderBy != null)
            {
                bool asc = settings.SortAscending == null || (bool)settings.SortAscending;
                switch (settings.OrderBy)
                {
                    case "Name":
                        return asc ? list.OrderBy(x => x.Name) : list.OrderByDescending(x => x.Name);

                    case "Username":
                        return asc ? list.OrderBy(x => x.Username) : list.OrderByDescending(x => x.Username);

                    case "Email":
                        return asc ? list.OrderBy(x => x.Email) : list.OrderByDescending(x => x.Email);

                    case "PhoneNumber":
                        return asc ? list.OrderBy(x => x.PhoneNumber) : list.OrderByDescending(x => x.PhoneNumber);
                }
            }

            return list.OrderBy(x => x.Name);
        }
        #endregion        
    }
}